package uk.ac.glasgow.demosocs.impl;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import uk.ac.glasgow.demosocs.Vote;
import uk.ac.glasgow.demosocs.Option;
import uk.ac.glasgow.demosocs.Candidate;
import uk.ac.glasgow.demosocs.ElectionState;
import uk.ac.glasgow.demosocs.ElectionResult;
import uk.ac.glasgow.demosocs.Election;
import uk.ac.glasgow.demosocs.users.User;
import java.io.Serializable;

/**
 * Concrete implementation of an election
 * @author Team K
 */
@SuppressWarnings("serial")
public class ElectionImpl implements Election, Serializable {
	private String eid;
	private String type;
	private String electorateID;
	
	private ElectionResult electionResult;
	private ElectionState electionState;
	private Set<Option> candidates = new HashSet<Option>();
	private Set<User> nominations = new HashSet<User>();
	private HashMap<String,Integer> votes = new HashMap<String,Integer>();

	private Set<Vote> rankedVotes = new HashSet<Vote>();
	
	private Date openNominations;
	private Date start;
	private Date end;
	
	/**
	 * Constructor
	 * @param name the electio's name
	 * @param typeID the election's type id
	 */
	public ElectionImpl( String name, String typeID ) {
		eid = name;
		type = typeID;		
	}
	
	/**
	 * Edits an election
	 * @param openN date nominations open
	 * @param start date voting opens
	 * @param end date voting ends
	 * @param eeID election id
	 */
	public void editElectionImpl( Date openN, Date start, Date end, String eeID ) {
		openNominations = openN;
		this.start = start;
		this.end = end;
		electorateID = eeID;
	}
	
	/**
	 * Getter: election id
	 */
	public String getEID() {
		return eid;
	}
	
	/**
	 * Getter: type ID
	 */
	public String getType() {
		return type;
	}
	
	/**
	 * Getter: electorateID
	 */
	public String getElectorateID() {
		return electorateID;	
	}
	
	/**
	 * Getter: election result
	 */
	public ElectionResult getElectionResult() {
		return electionResult;
	}
	
	/**
	 * Constructs and returns an election state.
	 */
	public ElectionState getElectionState() {
		SimpleDateFormat df = new SimpleDateFormat("d/M/y HH:mm");
		Date today = new Date();
		if(end.before(today)) {
			if(electionResult != null) return ElectionState.PUBLISHED;
			else return ElectionState.ENDED;
		} else if(start.before(today)) {
			return ElectionState.STARTED;
		} else if(openNominations.before(today)) {
			return ElectionState.NOMINATIONS_OPEN;
		} else if(today.before(openNominations)) {
			return ElectionState.NOT_STARTED;
		}
		return ElectionState.ENDED;
	}
	
	public Set<Vote> getVote() {
		return rankedVotes;
	}
	
	/**
	 * Returns a set of election candidates
	 */
	public Set<Candidate> getCandidates() {
		HashSet<Candidate> candidateList = new HashSet<Candidate>();
		for(Option o: candidates) {
			candidateList.add((Candidate) o);
		}
		return candidateList;
	}
	
	/**
	 * Returns a set of users which are nominees
	 */
	public Set<User> getNominations(){
		return nominations;
	}
	
	/**
	 * Gets the date nominations open
	 */
	public Date getOpenNominations() {
		return openNominations;
	}
	
	/**
	 * Gets the date voting starts
	 */
	public Date getStart() {
		return start;
	}
	
	/**
	 * Gets the date voting ends
	 */
	public Date getEnd() {
		return end;
	}
	
	/**
	 * Returns a hashmap of votes, key being the GUID and value being number of votes
	 */
	public HashMap<String, Integer> getVotes(){ 
		return votes;
	}
	
	/**
	 * Sets the election result to the on supplied
	 * @param an election result
	 */
	public void setElectionResult(ElectionResult er) {
		electionResult = er;
	}
	
	/**
	 * Sets the date nominations open
	 * @param onDate the date
	 */
	public void setOpenNominations(Date onDate) {
		openNominations = onDate;
	}
	
	/**
	 * Sets the start date.
	 */
	public void setStart(Date startDate) {
		start = startDate;
	}
	
	/**
	 * Sets the end date.
	 */
	public void setEnd(Date endDate) {
		end = endDate;
	}
	
	/**
	 * Sets the electorate ID
	 */
	public void setElectorateId(String electorate) {
		electorateID = electorate;
	}
	
	/**
	 * returns if elections are open
	 * @return true if nominations are open, false otherwise
	 */
	public boolean nominationsOpen(){
		return electionState == ElectionState.NOMINATIONS_OPEN;
	}
	
	/**
	 * Adds a vote for the supplied candidate GUID
	 * @param guid the candidate to vote for
	 */
	public void addVote(String guid){
		int numberOfVotes = 1;
		if (votes.containsKey(guid)){
			numberOfVotes = votes.remove(guid) + 1;
		}
		votes.put(guid, numberOfVotes);
	}
	
	public void addVote(Vote v)
	{
		rankedVotes.add(v);
	}
	
	/**
	 * Adds a user as a nominee
	 * @param u the user
	 */
	public void addNomination(User u){
		if(!type.equalsIgnoreCase("P"))
			nominations.add(u);
	}
	
	/**
	 * Adds a user as a candidate
	 * @param c the candidate
	 */
	public void addCandidate(Option e){
		candidates.add(e);
	}

}
